#include "utils.hpp"
namespace chatroom{
    // 1  brpc服务端 接口的实现
    class UserServiceImp : public chatroom::UserService{
        public:
            // chatroom::session::Ptr _redis_session;
            // chatroom::Status::Ptr _redis_status;
            // chatroom::Code::Ptr _redis_code;
            // chatroom::ESuser::Ptr _es_user;
            // chatroom::UserTable::Ptr _mysql_user;
            // //RPC调用文件上传与下载子服务
            // std::string _fileserver_name;
            // //基于短信验证平台
            // chatroom::DMSClient::Ptr  _DMS_client;
            // //服务信道管理
            // chatroom::ServiceManage::Sptr _mm_channel;
            UserServiceImp(chatroom::session::Ptr& redis_session,
            chatroom::Status::Ptr& redis_status,chatroom::Code::Ptr& redis_code
            ,chatroom::ESuser::Ptr& es_user,chatroom::UserTable::Ptr &mysql_user,
            std::string& fileserver_name,chatroom::DMSClient::Ptr& DMS_client,
            chatroom::ServiceManage::Sptr& mm_channel):
            _redis_session(redis_session),_redis_status(redis_status),_redis_code(redis_code),
            _es_user(es_user),_mysql_user(mysql_user),_DMS_client(DMS_client),_mm_channel(mm_channel){}
            ~UserServiceImp(){}
            //检查昵称是否合法
            bool check_name(const std::string& name)
            {
                if(name.size()<3 || name.size()>15)
                {
                    Logger_ERROR("昵称长度不符合要求");
                    return false;
                }
                for(int i = 0;i < name.size();i++)
                {
                    auto ret = name[i];
                    if(ret<'0'|| ret>'9'||ret < 'a'||ret > 'z' ||ret < 'A'||ret > 'Z' || ret!='-' || ret !='_')
                    {
                        Logger_ERROR("昵称字符不符合要求");
                        return false;
                    }
                }
                return true;
            }
            //检查密码是否合法
            bool check_passwd(const std::string& password)
            {
                if(password.size()<6 || password.size()>15)
                {
                    Logger_ERROR("密码长度不符合要求");
                    return false;
                }
                for(auto& ret:password)
                {
                    if(ret<'0'||ret>'9'||ret < 'a'||ret > 'z' ||ret < 'A'|| ret > 'Z')
                    {
                        Logger_ERROR("密码字符不符合要求");
                        return false;
                    }
                }
                return true;
            }

            //用户注册接口的实现
            virtual void UserRegister(::google::protobuf::RpcController* controller,
                                const ::chatroom::UserRegisterReq* request,
                                ::chatroom::UserRegisterRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    //1 . 从请求中取出昵称和密码
                                    std::string nickname = request->nickname();
                                    std:::string password = request->password();
                                    //2. 检查昵称是否合法（只能包含字母，数字，连字符-，下划线_，长度限制 3~15 之间）
                                    bool ret = check_name(nickname);
                                    if(!ret) {
                                        Logger_ERROR("{},昵称不合法{}",request->request_id(),nickname)
                                        errmsg_response(request->request_id(),"昵称不合法");
                                        return;
                                    }
                                    //3. 检查密码是否合法（只能包含字母，数字，长度限制 6~15 之间）
                                    ret = check_passwd(password);
                                    if(!ret)
                                    {
                                        Logger_ERROR("{},密码不合法{}",request->request_id(),password);
                                        return errmsg_response(request->request_id(),"密码不合法");
                                    }
                                    //4. 根据昵称在数据库进行判断是否昵称已存在
                                    auto user = _mysql_user->select_by_nickname(nickname);
                                    if(user){
                                        Logger_ERROR("{},用户名已经被占用了{}",request->request_id(),nickname);
                                        return errmsg_response(request->request_id(),"用户已经存在");
                                        
                                    }
                                    //5. 向数据库新增数据
                                    std::string uid = uuid();
                                    User user1(uid,nickname,password);
                                    bool ret = _mysql_user->insert(std::make_shared<chatroom::User>(user1));
                                    if(!ret)
                                    {
                                        Logger_ERROR("{},向SQL数据库新增数据失败",request->request_id());
                                        return errmsg_response(request->request_id(),"向SQL数据库新增数据失败");
                                    }
                                    //6. 向 ES 服务器中新增用户信息
                                    ret = _es_user->appendData(uid,nickname);
                                    if(!ret)
                                    {
                                        Logger_ERROR("{},向ES中新增数据失败",request->request_id());
                                        return errmsg_response(request->request_id(),"向ES中新增数据失败");
                                    }
                                    //7. 组织响应，进行成功与否的响应即可
                                    response->set_request_id(request->request_id());
                                    response->set_success(true);
                                }
            //用户登录状态接口的实现
            virtual void UserLogin(::google::protobuf::RpcController* controller,
                                const ::chatroom::UserLoginReq* request,
                                ::chatroom::UserLoginRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    //1. 从请求中取出昵称和密码
                                    std::string nickname = request->nickname();
                                    std::string password = request->password();
                                    //2. 通过昵称获取用户信息，进行密码是否一致的判断
                                    auto user = _mysql_user->select_by_nickname(nickname);
                                    if(user->get_passwd()!=password)
                                    {
                                        Logger_ERROR("{}密码输入错误",request->request_id());
                                        return errmsg_response(request->request_id(),"密码输入错误");
                                    }
                                    //3. 根据 redis 中的登录标记信息是否存在判断用户是否已经登录。
                                    bool ret = _redis_status->exist(user->get_user_id());
                                    if(!ret)
                                    {
                                        Logger_ERROR("{}该用户已经登录了",request->request_id());
                                        return errmsg_response(request->request_id(),"用户已经登录了");                                       
                                    }
                                    //4. 构造会话 ID，生成会话键值对，向 redis 中添加会话信息以及登录标记信息
                                    std::string ssid = uuid();
                                    ret = _redis_session->append(ssid,user->get_user_id());
                                    if(!ret)
                                    {
                                        Logger_ERROR("{}构建会话ID失败",request->request_id());
                                        return errmsg_response(request->request_id(),"构建会话ID失败");
                                    }
                                    _redis_status.append(user->get_user_id());
                                    //5. 组织响应，返回生成的会话 ID
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                    response->set_login_session_id(ssid);
                                }
            bool check_phonenum(const std::string& phonenum)
            {
                if(phonenum.size()!=11)
                {
                    Logger_ERROR("电话号码长度不对");
                    return false;
                }
                if(phonenum[0]!='1')
                {
                    Logger_ERROR("电话号码第一位错了");
                    return false;
                }
                if(phone[1] < 3 || phone[1] > 9)
                {
                    Logger_ERROR("电话号码第二位错了");
                    return false;
                }
                for(int i = 2;i < 11;i++)
                {
                    if(phonenum[i]<0 || phonenum[i]>9)
                    {
                        return false;
                    }
                }
                return true;
            }
            //获取手机验证码
            virtual void GetPhoneVerifyCode(::google::protobuf::RpcController* controller,
                                const ::chatroom::PhoneVerifyCodeReq* request,
                                ::chatroom::PhoneVerifyCodeRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    //1. 从请求中取出手机号码
                                    std::string phone = request->phone_number();
                                    //2. 验证手机号码格式是否正确（必须以 1 开始，第二位 3~9 之间，后边 9 个数字字符）
                                    bool ret = check_phonenum(phone);
                                    if(!ret)
                                    {
                                        Logger_ERROR("{}电话号码不正确",request->request_id());
                                        errmsg_response(request->request_id(),"电话号码不正确");
                                        return;
                                    }
                                    //3. 生成 4 位随机验证码
                                    std::string code = vcode();
                                    //4. 基于短信平台 SDK 发送验证码
                                    bool ret = _DMS_client->send(phone,code);
                                    if(!ret)
                                    {
                                        Logger_ERROR("{}验证码发送失败",request->request_id());
                                        return errmsg_response(request->request_id(),"验证码发送失败");
                                    
                                    }
                                    //5. 构造验证码 ID，添加到 redis 验证码映射键值索引中
                                    std::string codeid = uuid();
                                    _redis_code.append(codeid,code);
                                    //6. 组织响应，返回生成的验证码 ID
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                    response->set_verify_code_id(codeid);
                                }
            //手机号注册 
            virtual void PhoneRegister(::google::protobuf::RpcController* controller,
                                const ::chatroom::PhoneRegisterReq* request,
                                ::chatroom::PhoneRegisterRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    //1. 从请求中取出手机号码和验证码
                                    std::string phonenum = request->phone_number();
                                    std::string code = request->verify_code();
                                    //2. 检查注册手机号码是否合法
                                    check_phonenum(phonenum);
                                    //3. 从 redis 数据库中进行验证码 ID-验证码一致性匹配
                                    auto ret = _redis_code->cid(request->verify_code_id());
                                    if(ret!=code)
                                    {
                                        Logger_ERROR("{}-{}不匹配",request->verify_code_id(),code);
                                        return errmsg_response(request->request_id(),"验证码-验证码ID不匹配");
                                    }
                                    //4. 通过数据库查询判断手机号是否已经注册过
                                    auto user = _mysql_user->select_by_phone(phonenum);
                                    if(user)
                                    {
                                        Logger_ERROR("{}该手机号已经注册过了",request->request_id());
                                        errmsg_response(request->request_id(),"手机号已经注册过了");
                                        return;
                                    }
                                    //5. 向数据库新增用户信息
                                    std::string uid =  uuid();
                                    User user1(uid,phonenum);
                                    bool judge = _mysql_user->insert(std::make_shared<chatroom::User>(user1));
                                    if(!judge)
                                    {
                                        Logger_ERROR("{}向sql数据库新增数据失败",request->request_id());
                                        return errmsg_response(request->request_id(),"向sql数据库新增数据失败");
                                    }
                                    //6. 向 ES 服务器中新增用户信息
                                    judge = _es_user->append(uid,"",phonenum);
                                    if(!judge)
                                    {
                                        Logger_ERROR("{}向ES中新增数据失败",request->request_id());
                                        return errmsg_response(request->request_id(),"向ES中新增数据失败");
                                    }
                                    //7. 组织响应，返回注册成功与否
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
            virtual void PhoneLogin(::google::protobuf::RpcController* controller,
                                const ::chatroom::PhoneLoginReq* request,
                                ::chatroom::PhoneLoginRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    //1. 从请求中取出手机号码和验证码 ID，以及验证码。
                                    std::string phonenum = request->phone_number();
                                    std::string code = request->verify_code();
                                    std::string vricode = request->verify_code_id();
                                    //2. 检查注册手机号码是否合法
                                    bool check = check_phonenum(phonenum);
                                    if(!check)
                                    {
                                        Logger_ERROR("{}手机号码不合法");
                                        return errmsg_response("手机号码不合法");
                                    }
                                    //3. 从 redis 数据库中进行验证码 ID-验证码一致性匹配
                                    auto ret = _redis_code->cid(vricode);
                                    if(ret!=code)
                                    {
                                        Logger_ERROR("验证码ID-验证码不匹配");
                                        errmsg_response(request->request_id(),("验证码ID-验证码不匹配");
                                        return;
                                    }
                                    //4. 根据手机号从数据数据进行用户信息查询，判断用用户是否存在
                                    auto user = _mysql_user->select_by_phone(phonenum);
                                    if(!user)
                                    {
                                        Logger_ERROR("用户不存在");
                                        errmsg_response(request->request_id(),"用户不存在");
                                        return;
                                    }
                                    //5. 根据 redis 中的登录标记信息是否存在判断用户是否已经登录。
                                    bool test = _redis_status->exist(user->get_user_id());
                                    if(test==true)
                                    {
                                        Logger_ERROR("该用户已经在其他地方登录");
                                        errmsg_response(request->request_id(),"该用户已经在其他地方登录");
                                        return;
                                    }
                                    //6. 构造会话 ID，生成会话键值对，向 redis 中添加会话信息以及登录标记信息
                                    std::string ssid = uuid();
                                    ret = _redis_session->append(ssid,user->get_user_id());
                                    if(!ret)
                                    {
                                        Logger_ERROR("{}构建会话ID失败",request->request_id());
                                        errmsg_response(request->request_id(),"构建会话ID失败");
                                        return;
                                    }
                                    _redis_status.append(user->get_user_id());
                                    //7. 组织响应，返回生成的会话 ID
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                    response->set_login_session_id(ssid);
                                }
            virtual void GetUserInfo(::google::protobuf::RpcController* controller,
                                const ::chatroom::GetUserInfoReq* request,
                                ::chatroom::GetUserInfoRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    //获取单个用户的信息
                                    Logger_DEBUG("收到获取单个用户信息的请求");
                                    // 1. 从请求中取出用户 ID
                                    std::string user_id = request->user_id();
                                    // 2. 通过用户 ID，从数据库中查询用户信息
                                    auto user = _mysql_user->select_by_userid(user_id);
                                    if(!user)
                                    {
                                        Logger_ERROR("{}该用户没有注册");
                                        return errmsg_response(request->request_id(),"该用户没有注册");
                                    }
                                    // 3. 根据用户信息中的头像 ID，从文件服务器获取头像文件数据，组织完整用户信息
                                    auto* userinfo = response->mutable_user_info();
                                    userinfo->set_user_id(user->get_user_id());
                                    userinfo->set_nickname(user->get_nickname());
                                    userinfo->set_description(user->get_desc());
                                    userinfo->set_phone(user->get_phone());
                                    if(!user->get_avatar_id().empty()){
                                        //调用文件子服务，获取文件头像
                                        // 1创建信道服务管理对象
                                        auto channel = _mm_channel->choose(_fileserver_name);
                                        if(!channel)
                                        {
                                            Logger_ERROR("{}没有存在对应的文件子服务节点",request->request_id());
                                            return errmsg_response(request->request_id(),"没有存在对应的文件子服务节点");
                                        }
                                        chatroom::FileService_Stub stub(channel.get());
                                        brpc::Controller* cntl = new brpc::Controller();
                                        chatroom::GetSingleFileReq req;
                                        chatroom::GetSingleFileRsp rsp;
                                        req.set_request_id(request->request_id());
                                        req.set_file_id(user->get_avatar_id());
                                        //同步调用子服务
                                        stub.GetSingleFile(cntl,req,rsp,nullptr);
                                        if(cntl->Failed())
                                        {
                                            Logger_ERROR("{}rpc调用失败,{}",request->request_id(),cntl->ErrorText());
                                            delete cntl;
                                            return errmsg_response(request->request_id(),"rpc调用失败");
                                        }
                                        userinfo->set_avatar(rsp.file_data().file_content());
                                    }
                                    // 4. 组织响应，返回用户信息
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
            //获取多个用户信息
            virtual void GetMultiUserInfo(::google::protobuf::RpcController* controller,
                                const ::chatroom::GetMultiUserInfoReq* request,
                                ::chatroom::GetMultiUserInfoRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    Logger_DEBUG("收到获取多个用户的请求");
                                    //获取用户列表
                                    std::vector<std::string> user_list;
                                    for(int i = 0; i< request->users_id_size();i++)
                                    {
                                        user_list.push_back(request->users_id(i));
                                    }
                                    //通过用户列表向数据库查询到用户信息
                                    auto users = _mysql_user->slelect_by_idlist(user_list);
                                    if(users.size()!=request->users_id_size())
                                    {
                                        Logger_ERROR("{}存在用户没有注册",request->request_id());
                                        return errmsg_response(request->request_id(),"存在用户没有注册");
                                    }
                                    
                                    
                                    //调用文件子服务，获取文件头像
                                    // 1创建信道服务管理对象
                                    auto channel = _mm_channel->choose(_fileserver_name);
                                    if(!channel)
                                    {
                                        Logger_ERROR("{}没有存在对应的文件子服务节点",request->request_id());
                                        return errmsg_response(request->request_id(),"没有存在对应的文件子服务节点");
                                    }
                                    chatroom::FileService_Stub stub(channel.get());
                                    brpc::Controller* cntl = new brpc::Controller();
                                    chatroom::GetMultiFileReq req;
                                    chatroom::GetMultiFileRsp rsp;
                                    req.set_request_id(request->request_id());
                                    for(auto& user:users)
                                    {
                                        req.add_file_id_list(user.get_avatar_id());
                                    }
                                    //同步调用子服务
                                    stub.GetMultiFile(cntl,req,rsp,nullptr);
                                    if(cntl->Failed())
                                    {
                                        Logger_ERROR("{}rpc调用失败,{}",request_>request_id(),cntl->ErrorText());
                                        delete cntl;
                                        return errmsg_response(request->request_id(),"rpc调用失败");
                                    }
                                    for(int i = 0;i < users.size();i++){
                                        //查询到之后，要向rsp中填充数据
                                        auto* map = response->mutable_users_info();
                                        auto* filemap = rsp->mutable_file_data();
                                        UserInfo info;
                                        info->set_user_id(users[i].get_user_id());
                                        info->set_nickname(users[i].get_nickname());
                                        info->set_description(users[i].get_desc());
                                        info->set_phone(users[i].get_phone());
                                        info->set_avatar((*filemap)[users[i].get_avatar_id()].file_content());
                                        // info->set_avatar(rsp.file_data(i));
                                        // (*map)[request->request_id()] = info;
                                        (*map)[info.user_id()] = info;
                                    }
                                    //返回响应
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
            //设置用户头像信息
            virtual void SetUserAvatar(::google::protobuf::RpcController* controller,
                                const ::chatroom::SetUserAvatarReq* request,
                                ::chatroom::SetUserAvatarRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    Logger_DEBUG("收到设置用户头像信息的请求");
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    // 1. 从请求中取出用户 ID 与头像数据
                                    std::string user_id = request->request_id();
                                    auto avatar = request->avatar();
                                    // 2. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
                                    auto user = _mysql_user->select_by_userid(user_id);
                                    if(!user)
                                    {
                                        Logger_ERROR("{}该用户没有注册",request->request_id());
                                        return errmsg_response(request->request_id(),"该用户没有注册");
                                    }
                                    // 3. 上传头像文件到文件子服务，
                                    // (1) 创建信道服务管理对象
                                    auto channel = _mm_channel->choose(_fileserver_name);
                                    if(!channel)
                                    {
                                        Logger_ERROR("{}没有存在对应的文件子服务节点",request->request_id());
                                        return errmsg_response(request->request_id(),"没有存在对应的文件子服务节点");
                                    }
                                    chatroom::FileService_Stub stub(channel.get());
                                    brpc::Controller* cntl = new brpc::Controller();
                                    chatroom::PutSingleFileReq req;
                                    chatroom::PutSingleFileRsp rsp;
                                    req.set_request_id(request->request_id());
                                    auto* filedata = request->mutable_file_data();
                                    file_data->set_filename(user->get_avatar_id());
                                    file_data->set_file_size(request->avatar().size());
                                    file_data->set_file_content(avatar);
                                    req.set_file_data(file_data);
                                    //同步调用文件上传子服务
                                    stub.PutSingleFile(cntl,&req,&rsp,nullptr);
                                    if(cntl->Failed())
                                    {
                                        Logger_ERROR("{}文件上传子服务调用失败,{}",request_>request_id(),cntl->ErrorText());
                                        delete cntl;
                                        return errmsg_response(request->request_id(),"文件上传子服务调用失败");
                                    }
                                    // 4. 将返回的头像文件 ID 更新到数据库中
                                    auto avatar_id = rsp.request_id();
                                    user->set_avatar_id(avatar_id);
                                    // 5. 更新 ES 服务器中用户信息
                                    // bool appendData(
                                    //     const std::string& usrid,
                                    //     const std::string& nickname,
                                    //     const std::string& desc,
                                    //     const std::string& phone,
                                    //     const std::string& avid){
                                    _es_user->appendData(user->get_user_id(),user->get_nickname(),user->get_desc(),user->get_phone(),user->get_avatar_id());
                                    // 6. 组织响应，返回更新成功与否
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
            //设置用户昵称
            virtual void SetUserNickname(::google::protobuf::RpcController* controller,
                                const ::chatroom::SetUserNicknameReq* request,
                                ::chatroom::SetUserNicknameRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    Logger_DEBUG("收到设置用户昵称的请求");
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    // 1. 从请求中取出用户 ID 与新的昵称
                                    std::string user_id = request->user_id();
                                    std::string newnickname = request->nickname();
                                    // 2. 判断昵称格式是否正确
                                    bool check = check_name(newnickname);
                                    if(!check)
                                    {
                                        Logger_ERROR("{}用户昵称格式不正确",request->request_id());
                                        return errmsg_response(request->request_id(),"用户昵称格式不正确");
                                    }
                                    // 3. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
                                    auto user = _mysql_user->select_by_userid(user_id);
                                    if(!user)
                                    {
                                        Logger_ERROR("{}用户没有注册",request->request_id());
                                        return errmsg_response(request->request_id(),"用户没有注册");
                                    }
                                    // 4. 将新的昵称更新到数据库中 先更改单个的字段
                                    user->set_nickname(newnickname);
                                    //更新到数据库中
                                    check = _mysql_user->update(user);
                                    if(!check)
                                    {
                                        Logger_ERROR("{}更新数据库失败",request->request_id());
                                        return errmsg_response(request->request_id(),"更新数据库失败");
                                    }
                                    // 5. 更新 ES 服务器中用户信息
                                    check = _es_user->appendData(user->get_user_id(),user->get_nickname(),user->get_desc(),user->get_phone(),
                                    user->get_avatar_id());
                                    if(!check)
                                    {
                                        Logger_ERROR("{}更新ES失败",request->request_id());
                                        return errmsg_response(request->request_id(),"更新ES失败");
                                    }
                                    // 6. 组织响应，返回更新成功与否
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
            //设置用户的个性签名
            virtual void SetUserDescription(::google::protobuf::RpcController* controller,
                                const ::chatroom::SetUserDescriptionReq* request,
                                ::chatroom::SetUserDescriptionRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    Logger_DEBUG("收到设置用户个性签名的请求");
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    // 1. 从请求中取出用户 ID 与新的签名
                                    std::string user_id = request->user_id();
                                    std::string newdesc = request->description();
                                    // 2. 从数据库通过用户 ID 进行用户信息查询，判断用户是否存在
                                    auto user = _mysql_user->select_by_userid(user_id);
                                    if(!user)
                                    {
                                        Logger_ERROR("{}用户没有注册",request->request_id());
                                        return errmsg_response(request->request_id(),"用户没有注册");
                                    }
                                    // 3. 将新的签名更新到数据库中
                                    user->set_desc(newdesc);
                                     //更新到数据库中
                                    bool check = _mysql_user->update(user);
                                    if(!check)
                                    {
                                        Logger_ERROR("{}更新数据库失败",request->request_id());
                                        return errmsg_response(request->request_id(),"更新数据库失败");
                                    }
                                    // 4. 更新 ES 服务器中用户信息
                                    check = _es_user->appendData(user->get_user_id(),user->get_nickname(),user->get_desc(),
                                    user->get_phone(),user->get_avatar_id());
                                    if(!check)
                                    {
                                        Logger_ERROR("{}更新ES失败",request->request_id());
                                        return errmsg_response(request->request_id(),"更新ES失败");
                                    }
                                    // 5. 组织响应，返回更新成功与否
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
            //设置用户的电话号码
            virtual void SetUserPhoneNumber(::google::protobuf::RpcController* controller,
                                const ::chatroom::SetUserPhoneNumberReq* request,
                                ::chatroom::SetUserPhoneNumberRsp* response,
                                ::google::protobuf::Closure* done)
                                {
                                    Logger_DEBUG("收到设置用户电话号码的请求");
                                    // 1. 从请求中取出手机号码和验证码 ID，以及验证码。
                                    brpc::ClosureGuard guard(done);
                                    auto errmsg_response = [this,response](const std::string& rid,const std::string& errmsg) ->void{
                                        response->set_success(false);
                                        response->set_request_id(rid);
                                        request->set_errmsg(errmsg);
                                        return;
                                    }
                                    std::string phone_number = request->phone();
                                    std::string phone_verify_code = request->phone_verify_code();
                                    std::string phone_verify_code_id = request->phone_verify_code_id();
                                    // 2. 检查注册手机号码是否合法
                                    bool test = check_phonenum(phone_number);
                                    if(!test)
                                    {
                                        Logger_ERROR("{}手机号码不合法",request->request_id());
                                        return errmsg_response(request->request_id(),"手机号码不合法");
                                    }
                                    // 3. 从 redis 数据库中进行验证码 ID-验证码一致性匹配
                                    auto code = _redis_code->cid(phone_verify_code_id);
                                    if(code!=phone_verify_code)
                                    {
                                        Logger_ERROR("{}验证码不一致",request->request_id());
                                        return errmsg_response(request->request_id(),"验证码不一致");
                                    }
                                    // 4. 根据手机号从数据数据进行用户信息查询，判断用用户是否存在
                                    auto user = _mysql_user->select_by_phone(phone_number);
                                    if(!user)
                                    {
                                        Logger_ERROR("{}用户没有注册",request->request_id());
                                        return errmsg_response(request->request_id(),"用户没有注册");
                                    }
                                    // 5. 将新的手机号更新到数据库中
                                    user->set_phone(phone_number);
                                        //更新到数据库中
                                    bool check = _mysql_user->update(user);
                                    if(!check)
                                    {
                                        Logger_ERROR("{}更新数据库失败",request->request_id());
                                        return errmsg_response(request->request_id(),"更新数据库失败");
                                    }
                                    // 4. 更新 ES 服务器中用户信息
                                    check = _es_user->appendData(user->get_user_id(),user->get_nickname(),user->get_desc(),
                                    user->get_phone(),user->get_avatar_id());
                                    if(!check)
                                    {
                                        Logger_ERROR("{}更新ES失败",request->request_id());
                                        return errmsg_response(request->request_id(),"更新ES失败");
                                    }
                                    // 5. 组织响应，返回更新成功与否
                                    response->set_success(true);
                                    response->set_request_id(request->request_id());
                                }
        private:
            chatroom::session::Ptr _redis_session;
            chatroom::Status::Ptr _redis_status;
            chatroom::Code::Ptr _redis_code;
            chatroom::ESuser::Ptr _es_user;
            chatroom::UserTable::Ptr _mysql_user;
            //RPC调用文件上传与下载子服务
            std::string _fileserver_name;
            //基于短信验证平台
            chatroom::DMSClient::Ptr  _DMS_client;
            //服务信道管理
            chatroom::ServiceManage::Sptr _mm_channel;
    };
    class UserServer{
        public:
            using ptr = std::shared_ptr<UserServer>;
            UserServer(chatroom::Discovery::ptr discover_server,
            std::shared_ptr<sw::redis::Redis>& redis_client,
            std::shared_ptr<elasticlient::Client>& es_client,
            std::shared_ptr<odb::core::database> sql_client,
            std::shared_ptr<brpc::Server> server,
            chatroom::Registery::ptr rclient)
            :_discover_server(discover_server)
            ,_redis_client(redis_client),_es_client(es_client)
            ,_sql_client(sql_client),_server(server),_rclient(rclient){}
            ~UserServer(){}
            void start(){
                _server->RunUntilAskedToQuit();
            }
        private:
            chatroom::Discovery::ptr _discover_server;
            std::shared_ptr<sw::redis::Redis>& _redis_client;
            std::shared_ptr<elasticlient::Client>& _es_client;
            std::shared_ptr<odb::core::database> _sql_client;
            std::shared_ptr<brpc::Server> _server;
            chatroom::Registery::ptr _rclient;
    };
    class UserServerBuild{
        public:
            //实例化redis客户端
            void make_redis_client(const std::string& host,
                int port,
                int db,
                bool keep_alive){
                    _redis_client = chatroom::RedisClientFactory::create(host,port,db,keep_alive);
            }
            //实例化sql客户端
            void make_sql_client(const std::string& user,
            const std::string &pswd,
            const std::string &host,
            const std::string &db,
            const std::string &cset,
            int port,
            int conn_pool_count){
                _sql_client = chatroom::DBFactory::create(user,pswd,host,db,cset,port,conn_pool_count);
            }
            //实例化ES客户端
            void make_es_client(const std::vector<std::string> &hostUrlList){
                _es_client = chatroom::ESclientFactory::create(hostUrlList);
            }
            void make_rpc_server(int32_t port,int32_t timeout,int32_t num_threads)
            {
                // chatroom::session::Ptr _redis_session;
                // chatroom::Status::Ptr _redis_status;
                // chatroom::Code::Ptr _redis_code;
                // chatroom::ESuser::Ptr _es_user;
                // chatroom::UserTable::Ptr _mysql_user;
                // //RPC调用文件上传与下载子服务
                // std::string _fileserver_name;
                // //基于短信验证平台
                // chatroom::DMSClient::Ptr  _DMS_client;
                //服务信道管理
               if(!_redis_client)
                {
                    Logger_ERROR("redis模块没有初始化");
                    abort();
                }
                if(!_es_client)
                {
                    Logger_ERROR("ES模块没有初始化");
                    abort();
                }
                if(!_sql_client)
                {
                    Logger_ERROR("sql数据库模块没有初始化");
                    abort();
                }
                if(!_DMS_client)
                {
                    Logger_ERROR("短信模块没有初始化");
                    abort();
                }
                if(!_mm_channel)
                {
                    Logger_ERROR("信道管理模块没有初始化");
                    abort();
                }
                _server = std::make_shared<brpc::Server>();
                //3 往服务器中添加服务
                UserServiceImp* User = new UserServiceImp(
                    make_shared<chatroom::session>(_redis_client),
                    make_shared<chatroom::Status>(_redis_client),
                    make_shared<chatroom::Code>(_redis_client),
                    make_shared<chatroom::ESuser>(_es_client),
                    make_shared<chatroom::UserTable>(_sql_client),
                    _fileserver_name,_DMS_client,_mm_channel
                );
                int ret = _server->AddService(User,brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
                if(ret == -1)
                {
                    Logger_ERROR("添加服务失败");
                    abort();
                }
                //4 启动服务器
                brpc::ServerOptions options;
                options.idle_timeout_sec = timeout; //指定超时连接时间，-1表示超时一直在连接着
                options.num_threads = num_threads;//cpu线程数量
                ret = _server->Start(port,&options);
                if(ret == -1)
                {
                    Logger_ERROR("启动服务器失败");
                    abort();
                }
            }
            //实例化服务发现
            void make_discover_object(const std::string& host,std::string& basedir,std::string& fileserver_name)
            {
                _mm_channel =  std::make_shared<ServiceManage>();
                _mm_channel->declare(fileserver_name);
                _fileserver_name = fileserver_name;
                auto put_cb = std::bind(&ServiceManage::ServiceOnline,_mm_channel.get(),std::placeholders::_1,std::placeholders::_2);
                auto off_cb = std::bind(&ServiceManage::ServiceOffline,_mm_channel.get(),std::placeholders::_1,std::placeholders::_2);
                _discover_client = make_shared<Discovery>(host,basedir,put_cb,off_cb);
            }
            void  make_reg_object(std::string& register_host,const std::string& hostname,std::string& access_host)
            {
                _rclient = std::make_shared<chatroom::Registery>(register_host);
                _rclient->Register(hostname,access_host);
            }
            void make_DMS_client(const std::string& key_id,const std::string& key_secret)
            {
               _DMS_client = std::make_shared<DMSClient>(key_id,key_secret);
            }

            UserServer::ptr build()
            {
                if(!_server){
                    Logger_ERROR("RPC服务子模块没有初始化");
                    abort();
                }
                if(!_rclient){
                    Logger_ERROR("注册服务没有初始化");
                    abort();
                }
                return std::make_shared<UserServer>(
                    _discover_client,_redis_client,_es_client,_sql_client,_server,_rclient
                );
            }
        private:
            std::shared_ptr<sw::redis::Redis> _redis_client;
            std::shared_ptr<elasticlient::Client> _es_client;
            std::shared_ptr<odb::core::database> _sql_client;
            std::shared_ptr<brpc::Server> _server;
            chatroom::Registery::ptr _rclient;
            chatroom::Discovery::ptr _discover_client;
            std::string _fileserver_name;
            chatroom::ServiceManage::Sptr _mm_channel;
            chatroom::DMSClient::Ptr _DMS_client
    };
}